///////////////////////////////////////////////////////////////////////////
// Programa VIDA. 
//
// Esto no es un ejemplo de buena programacion ni esta optimizado
// en velocidad, solo tiene por proposito ilustrar sobre el algoritmo
// de VIDA. Es decir, esta pensado para ser facil de entender.
//
///////////////////////////////////////////////////////////////////////////
#include <stdio.h>             // funciones de impresion
#include <stdlib.h>            // funciones aleatorias
#include <unistd.h>            // si queremos poner delays
#include "string.h"

#define FILAS 80
#define COLS 80 

#define CELULA 1               // No modificar

int pantalla[FILAS][COLS/32 + 1];    // Pantalla del ciclo anterior
int pantalla2[FILAS][COLS/32 + 1];   // Pantalla del ciclo actual

///////////////////////////////////////////////////////////////////////////
//      Inicializa la matriz de pantaala y pone las celulas iniciales
///////////////////////////////////////////////////////////////////////////
// void IniciarPantalla(void)
// {
// unsigned int col, fila;
// 
// for(col=0; col<COLS; col++)                // Exploramos todo el mundo
// 	for(fila=0; fila<FILAS; fila++)
// 		pantalla2[fila][col]=0;              // Pomemos el mundo a 0 (vacio)
// 
// for(int n=0; n<POBLACION_INICIAL; n++)     // Siembra celulas aleatoriamente
// 	{
// 	fila=random(FILAS);
// 	col=random(COLS);
// 	pantalla2[fila][col]=CELULA;
// 	}
// }

#define OCUPADA(fila, col) ( (pantalla[fila][col/32] & (1 << col) ) ? 1 : 0)
#define OCUPADA2(fila, col) ( (pantalla2[fila][col/32] & (1 << col) ) ? 1 : 0)


///////////////////////////////////////////////////////////////////////////
//   Calcula si la celulas se reproducen, viven o mueren
///////////////////////////////////////////////////////////////////////////

#define XSIZE (COLS/32+1)

#define FOR_BODY	\
					vecinos = 0;	\
					vecinos += (upper & 1); \
					vecinos += (upper & 2) >> 1; \
					vecinos += (upper & 4) >> 2; \
					vecinos += (lower & 1); \
					vecinos += (lower & 2) >> 1; \
					vecinos += (lower & 4) >> 2; \
					vecinos += (current & 1); \
					vecinos += (current & 4) >> 2; \
					if ( current & 2) { \
						if (vecinos>1 && vecinos<4) { \
							write_data |= write_mask; \
						} else { \
							write_data &= ~write_mask; \
						} \
					} else { \
						if (vecinos == 3) { \
							write_data |= write_mask; \
						} \
					} \
					current >>= 1; \
					lower >>= 1; \
					upper >>= 1;
					


void CicloVida(void)
{
register int col, fila;

for(fila=0; fila<FILAS; fila++)
  for(col=0; col<XSIZE; col++)              // Exploramos todo el mundo
		{
			
		register int upper;
		register int current;
		register int lower;
		register int vecinos = 0;
		register int write_data;
			
		current = pantalla[fila][col] ;
		upper = pantalla[ (fila+FILAS-1) % FILAS ][col];
		lower = pantalla[ (fila+1) % FILAS ][col];
			
		// procesar primera celda del bucket (bit menos significativo)
		// este es un caso especial porque implica leer vecinos
		// que se encuentran en buckets de otras columnas
		vecinos += OCUPADA( (fila+FILAS-1) % FILAS, ((col*32)+COLS-1) % COLS );
		vecinos += (upper & 1);
		vecinos += ( (upper>>1) & 1);	// contar los dos bits menos significativos

		vecinos += OCUPADA( fila, ((col*32)+COLS-1) % COLS);
		vecinos += ( (current>>1) & 1 );	// contar la celda a la derecha

		vecinos += OCUPADA( (fila+1) % FILAS, ((col*32)+COLS-1) % COLS );
		vecinos += (lower & 1);
		vecinos += ( (lower>>1) & 1);		// contar los dos bits menos significativos
		
		if (current & 1) {
			if (vecinos>1 && vecinos<4) { // tiene mas de dos vecinos y menos de cuatro
				write_data = 1;
			} else {
				write_data = 0;
			}
		} else {
			if (vecinos == 3) {
				write_data = 1;
			} else {
				write_data = 0;
			}
		}
			
			
		{
			register int write_mask = 1;
			
			if (col == XSIZE-1) {
				// procesar celdas del 1 al 31
				for (write_mask = 2;write_mask != (1 << COLS%32-1) ; write_mask <<= 1) {
					FOR_BODY;
				}
			} else {
				for (write_mask = 2; write_mask != (1 << 31) ; write_mask <<= 1) {
					FOR_BODY;
				}
				
			}
			
			// procesar el ultimo bit de mas a la derecha del bucket (mas significativo)
			vecinos = 0;
			
			vecinos += (upper & 1);
			vecinos += (upper & 2) >> 1;
			vecinos += pantalla[(fila+FILAS-1) % FILAS][(col+1) % XSIZE]  & 1;
			
			vecinos += (lower & 1);
			vecinos += (lower & 2) >> 1;
			vecinos += pantalla[(fila+1) % FILAS][(col+1) % XSIZE]  & 1;
			
			vecinos += (current & 1);
			vecinos += pantalla[fila][(col+1) % XSIZE]  & 1;

			if ( current & 2) {
				// si esta ocupada la celda... (segundo bit menos significativo)
				if (vecinos>1 && vecinos<4) { // tiene mas de dos vecinos y menos de cuatro
					write_data |= write_mask;
				} else {
					write_data &= ~write_mask;
				}
			} else {
				if (vecinos == 3) {
					// nace una celula
					write_data |= write_mask;
				}
			}

			
			pantalla2[fila][col] = write_data;
			
		}
			
			
		}
		
}


void CicloVida_2(void)
{
register int col, fila;

for(fila=0; fila<FILAS; fila++)
  for(col=0; col<XSIZE; col++)              // Exploramos todo el mundo
		{
			
		register int upper;
		register int current;
		register int lower;
		register int vecinos = 0;
		register int write_data;
			
		current = pantalla2[fila][col] ;
		upper = pantalla2[ (fila+FILAS-1) % FILAS ][col];
		lower = pantalla2[ (fila+1) % FILAS ][col];
			
		// procesar primera celda del bucket (bit menos significativo)
		// este es un caso especial porque implica leer vecinos
		// que se encuentran en buckets de otras columnas
		vecinos += OCUPADA2( (fila+FILAS-1) % FILAS, ((col*32)+COLS-1) % COLS );
		vecinos += (upper & 1);
		vecinos += ( (upper>>1) & 1);	// contar los dos bits menos significativos

		vecinos += OCUPADA2( fila, ((col*32)+COLS-1) % COLS);
		vecinos += ( (current>>1) & 1 );	// contar la celda a la derecha

		vecinos += OCUPADA2( (fila+1) % FILAS, ((col*32)+COLS-1) % COLS );
		vecinos += (lower & 1);
		vecinos += ( (lower>>1) & 1);		// contar los dos bits menos significativos
		
		if (current & 1) {
			if (vecinos>1 && vecinos<4) { // tiene mas de dos vecinos y menos de cuatro
				write_data = 1;
			} else {
				write_data = 0;
			}
		} else {
			if (vecinos == 3) {
				write_data = 1;
			} else {
				write_data = 0;
			}
		}
			
			
		{
			register int write_mask;
			
			if (col == XSIZE-1) {
				// procesar celdas del 1 al 31
				for (write_mask = 2;write_mask != (1 << COLS%32-1) ; write_mask <<= 1) {
					FOR_BODY;
				}
			} else {
				for (write_mask = 2; write_mask != (1 << 31) ; write_mask <<= 1) {
					FOR_BODY;
				}
				
			}
			
			// procesar el ultimo bit de mas a la derecha del bucket (mas significativo)
			vecinos = 0;
			
			vecinos += (upper & 1);
			vecinos += (upper & 2) >> 1;
			vecinos += pantalla2[(fila+FILAS-1) % FILAS][(col+1) % XSIZE]  & 1;
			
			vecinos += (lower & 1);
			vecinos += (lower & 2) >> 1;
			vecinos += pantalla2[(fila+1) % FILAS][(col+1) % XSIZE]  & 1;
			
			vecinos += (current & 1);
			vecinos += pantalla2[fila][(col+1) % XSIZE]  & 1;

			if ( current & 2) {
				// si esta ocupada la celda... (segundo bit menos significativo)
				if (vecinos>1 && vecinos<4) { // tiene mas de dos vecinos y menos de cuatro
					write_data |= write_mask;
				} else {
					write_data &= ~write_mask;
				}
			} else {
				if (vecinos == 3) {
					// nace una celula
					write_data |= write_mask;
				}
			}

			
			pantalla[fila][col] = write_data;
			
		}
			
			
		}
		
}


// void DespedidaYCierre(void)
// {
// clrscr();
// cprintf("\r\n\r\n\r\n");
// cprintf("                IMPLEMENTACION DEL PROGRAMA VIDA\r\n");
// cprintf("\r\n");
// cprintf("   Bueno, esto es todo. Este programa esta en modo texto y no tiene\r\n");
// cprintf(" ninguna optimizacion en velocidad, ni siquiera la implementacion\r\n");
// cprintf(" esta pensada para ser veloz. Su unico proposito es ilustrar sobre\r\n");
// cprintf(" algoritmo del programa. De las multiples implementaciones de este\r\n");
// cprintf(" esta es seguramente la mas sencilla de entender.\r\n\r\n");
// cprintf("   Programado por SILICOn, y puesto bajo licencia GNU. Modificalo y\r\n");
// cprintf(" distribuyelo como gustes, pero no te olvides de citar la fuente.\r\n");
// cprintf("\r\n");
// cprintf("\r\n");
// cprintf("   NOTA: Es mi primera excursion en la vida artificial.\r\n");
// }

int leerDatosIniciales()
{
  int fila, col;
  char inputc;
	
 for(col=0; col<COLS/32+1; col++)                // Exploramos todo el mundo
 	for(fila=0; fila<FILAS; fila++)
 		pantalla[fila][col]=0;              // Pomemos el mundo a 0 (vacio)
	
  for(col=0; col<COLS; col++)
	{
	for(fila=0; fila<FILAS; fila++)
	  {
	  inputc = getchar();
		  
	  switch (inputc) {
		case ' ': pantalla[fila][col/32] &= ~(1 << col);	// poner el bit a 0
				  break;
		case '@': pantalla[fila][col/32] |= (1 << col);	// poner el bit a 1
				  break;
		default:  fprintf(stderr, "Error en el archivo de entrada: fila %d contiene un caracter invalido en la columna %d \n", fila, col);
				  return(-1);
		}
	  }
	if(getc(stdin)!='\n')
	  {
	  fprintf(stderr, "Error en el archivo de entrada: fila %d no termina en \\n \n", fila);
	  return(-1);
	  }
	}
	return(0);
}

void grabarMatriz(int ppantalla[FILAS][COLS/32 + 1], int iteracion)
{
  int fila, col;
  char filename[20];
	
  char strcolumna[FILAS+1];
	
  FILE *archivo;
  
  sprintf(filename, "out/salida%03d.txt",iteracion);
  archivo = fopen(filename, "w");
  for(col=0; col<COLS; col++)   
	{
		for(fila=0; fila<FILAS; fila++) {
			strcolumna[fila] = (ppantalla[fila][col/32] & (1 << col) )  ?'@':' ';
		}
		strcolumna[FILAS] = '\n';
		
		fwrite(strcolumna, FILAS+1, 1, archivo);
	}
  fclose(archivo);
}

///////////////////////////////////////////////////////////////////////////
// La funcion principal
///////////////////////////////////////////////////////////////////////////
int main(int argc, char *argv[])
{
unsigned int ciclo;        // Ciclo de vida actual

leerDatosIniciales(); //vienen por stdin
for(ciclo=0;ciclo<200;ciclo++)              // iteramos unas 400 veces
	{
		
		CicloVida();              // Calculamos un ciclo de vida
		grabarMatriz(pantalla2, ciclo*2);  // Sacamos el resultado por stdout
		
		CicloVida_2();
		grabarMatriz(pantalla, ciclo*2+1);  // Sacamos el resultado por stdout
	
	}
/*DespedidaYCierre();*/
return(0);
}


















